#include "N2DrawFunctions.h"
#include <math.h>

void N2DrawLine(N2Points *pnts, uint32 cnt, N2SimpleDrawer *draw) 
{
    uint32 i;
    for(i=0,cnt--; i<cnt; i++)
    {
        N2DrawLine(pnts[i].x, pnts[i].y, pnts[i+1].x, pnts[i+1].y, nColor, pDraw);
    }
}

void N2DrawLineWithWidth(N2Points *pnts, uint32 cnt, long nWidth, N2SimpleDrawer *draw) 
{
    uint32 i;
    for(i=0,cnt--; i<cnt; i++)
    {
        N2DrawLineWithWidth(pnts[i].x, pnts[i].y, pnts[i+1].x, pnts[i+1].y, nWidth, nColor, pDraw);
    }
}

void N2DrawRect(long x1, long y1, long x2, long y2, N2SimpleDrawer *draw) 
{
    long t;
    if(y2<y1) { t = y1; y1 = y2; y2 = t; }
    if(x2<x1) { t = x1; x1 = x2; x2 = t; }
    for(long j=y1;j<=y2;j++)
        for(long i=x1;i<=x2;i++)
            pDraw->VirtDraw_SetPixel(i, j);
}

void N2DrawLine(long nX1, long nY1, long nX2, long nY2, N2SimpleDrawer *draw) 
{
    if(nX1>nX2) 
    {
        long Temp(nX1);
        nX1 = nX2;
        nX2 = Temp;

        Temp = nY1;
        nY1 = nY2;
        nY2 = Temp;
    }
    enum {UP, DOWN, R_UP, R_DOWN} LineCase;

    long n, C1, C2;

    long dx = abs(nX2-nX1);
    long dy = abs(nY2-nY1);

    long dc1;
    double dC2, delta;

    if(dx<dy) 
    {
        n = dy;
        C1  = nY1;
        dC2 = C2 = nX1;
        if(nY2>nY1) 
        {
            dc1 = 1;
            LineCase = UP;
        } else {
            dc1 = -1;
            LineCase = DOWN;
        }
        delta = double(dx)/dy;
    } else {
        n = dx;
        C1 = nX1;
        dC2 = C2 = nY1;
        dc1 = 1;
        if(nY2>nY1) 
        {
            LineCase = R_UP;
            delta = double(dy)/dx;
        } else {
            LineCase = R_DOWN;
            delta = -double(dy)/dx;
        }

        if(dx+dy==0) n = 1;
    }

    //C1 - one pixel shift
    //C2 - less then pixel shift
    for(long i=0;i<n;i++) 
    {
        C2 = (long)dC2;
        if(LineCase==UP || LineCase==DOWN) 
        {
            pDraw->VirtDraw_SetPixel(C2, C1, nColor);
            ;//DrawPixel(C2, C1);
        } else { //if(LineCase==R_UP || LineCase==R_DOWN)
            ;//DrawPixel(C1, C2);
            pDraw->VirtDraw_SetPixel(C1, C2, nColor);
        }

        C1  += dc1;
        dC2 += delta;
    }
}


void DrawLineWithWidth(long nX1, long nY1, long nX2, long nY2, long nWidth, N2SimpleDrawer *draw) 
{
  if(nX1>nX2) {
    long Temp(nX1);
    nX1 = nX2;
    nX2 = Temp;

    Temp = nY1;
    nY1 = nY2;
    nY2 = Temp;
  }
  enum {UP, DOWN, R_UP, R_DOWN} LineCase;

  long n;
  long C1, C2;

  long dx = abs(nX2-nX1);
  long dy = abs(nY2-nY1);

  long dc1;
  double dC2, delta;

  if(dx<dy) {
    n = dy;
    C1  = nY1;

    dC2 = C2 = nX1;
    if(nY2>nY1) {
      dc1 = 1;
      LineCase = UP;
    } else {
      dc1 = -1;
      LineCase = DOWN;
    }
    delta = double(dx)/dy;
  } else {
    n = dx;
    C1 = nX1;
    dC2 = C2 = nY1;
    dc1 = 1;
    if(nY2>nY1) {
      LineCase = R_UP;
      delta = double(dy)/dx;
    } else {
      LineCase = R_DOWN;
      delta = -double(dy)/dx;
    }

    if(dx+dy==0) n = 1;
  }

  long W2 = (nWidth>>1);

  //edge calculation
  if(nWidth>1) {
    n += nWidth;
    C1 -= W2 * dc1;
  } else nWidth = 1;

  for(long i=0;i<n;i++) {
    C2 = (long)dC2;

    long var = C2 - W2;
    for(long j=0;j<nWidth;j++,var++) {
      if(LineCase==UP || LineCase==DOWN) {
        pDraw->VirtDraw_SetPixel(var, C1, nColor);
      } else {
        pDraw->VirtDraw_SetPixel(C1, var, nColor);
      }
    }

    C1  += dc1;
    dC2 += delta;
  }
}

//////////////////////////////////////////////////////////////////////
HANDLE FindIntersect(long nX, LPPOINT pPoints, DWORD nCount) {
  long i;
  static CDWordArray *pnarInt = new CDWordArray;
  CPoint p1, p2, tmp;
  long nY;
  long nNum = 0;
  long nMax;

  if(pnarInt->GetSize()<1000)
    pnarInt->SetSize(1000);

  if(pPoints->x==(pPoints+nCount-1)->x && pPoints->y==(pPoints+nCount-1)->y)
    nMax = nCount - 1;
  else
    nMax = nCount;

  for(i=0;i<nMax;i++) {
    p1 = *(pPoints+i);
    if(i==nMax-1)
      p2 = *pPoints;
    else
      p2 = *(pPoints+i+1);


    if(p1.x > p2.x) {
      p2 = *(pPoints+i);
      if(i==nMax-1)
        p1 = *pPoints;
      else
        p1 = *(pPoints+i+1);
    }
    if(p1.x<=nX && p2.x>nX) { //find intersection Y
      if(p1.x==p2.x) {
        if(p1.y==p2.y) continue;
        pnarInt->SetAt(++nNum, p1.y);
        pnarInt->SetAt(++nNum, p2.y);
        //pnarInt->Add(p1.y);
        //pnarInt->Add(p2.y);
        continue;
      }

      double dy = double(nX-p1.x)*double(p2.y-p1.y)/double(p2.x-p1.x);
      //if((dy-long(dy))>0.5)
        dy -= 0.5;
      nY = p1.y + long(dy);
      //nY = double(p2.y) - double(p2.x-nX)*double(p2.y-p1.y)/double(p2.x-p1.x) + 1;

      pnarInt->SetAt(++nNum, nY);

      
      //pnarInt->Add(nY);
    }
  }
  pnarInt->SetAt(0, nNum);
  return pnarInt;
}

static int __cdecl Sort(const void *elem1, const void *elem2 ) {
  int ret = 0;
  if(*(long*)elem1<*(long*)elem2)
    ret = -1;
  else if(*(long*)elem1>*(long*)elem2)
    ret = 1;
  return ret;
}

void PaintByIntersect(long nX, HANDLE Intersect, N2SimpleDrawer *draw) {
  CDWordArray *pnarInt = (CDWordArray*)Intersect;
  long i, n = pnarInt->GetSize();
  CPoint p, p2[2];
  long nMin, nMax;
  
  n = pnarInt->GetAt(0);

  qsort(pnarInt->GetData()+1, n, 4, Sort);

  //ASSERT(!(n&1));
  BOOL bErr = n<1;
  if(bErr) 
    return;

  if(n>4)
    n = n;

  BOOL bDraw = TRUE;
  for(i=1;i<n;i++) {
    if(bDraw) {
      nMax = pnarInt->GetAt(i+1);
      nMin = pnarInt->GetAt(i);
      if(nMax<nMin) {
        long nTemp = nMin;
        nMin = nMax;
        nMax = nTemp;
        //nMin = pnarInt->GetAt(i+1);
        //nMax = pnarInt->GetAt(i);
      }
      if(nMax>=0) {
        if(nMin<0)
          nMin = 0;
        //p2[0].x = nX;
        //p2[0].y = nMin;
        //p2[1].x = nX;
        //p2[1].y = nMax;

        //DrawLine(p2[0].x, p2[0].y, p2[1].x, p2[1].y, nColor, pDraw);
        //CommonDrawGDI::DrawLine(dc, (LPPOINT)&p2, 2, nColor, 0, 1);
        for(long j = nMin; j <= nMax; j++) {
          //p.x = nX;
          //p.y = j;
          //CommonDrawGDI::DrawPixel(dc, &p, nColor);
          pDraw->VirtDraw_SetPixel(nX, j, nColor);
        }
      }
      bDraw = FALSE;
    } else bDraw = TRUE;
  }
  //delete pnarInt;
}

void DrawShape(LPPOINT pPoints, DWORD nCount, N2SimpleDrawer *draw) 
{
    long i;
    HANDLE Intersect;
    long nWidth = pDraw->VirtDraw_GetWidth();

    long nLeft = nWidth, nRight = 0;
    for(i=0;i<(long)nCount;i++) 
    {
        if((pPoints+i)->x<nLeft)
            nLeft = (pPoints+i)->x;
        if((pPoints+i)->x>nRight)
            nRight = (pPoints+i)->x;
        if(nLeft<=0 && nRight>=nWidth) break;
    }
    if(nLeft<0)
        nLeft = 0;
    if(nRight>nWidth)
        nRight = nWidth;

    for(i=nLeft;i<nRight;i++) 
    {
        Intersect = FindIntersect(i, pPoints, nCount);
        PaintByIntersect(i, Intersect, pDraw, nColor);
    }
}

//function written by Alexej Piskorsky (Alexp)
//specially for me
void N2DrawEllipse(N2Coord x, N2Coord y, uint32 nPrim, uint32 nSec, 
                   double Angle, N2Drawer *draw, uint32 nWidth, BOOL bFill)
{
    double Rad = 3.1415926*Angle/180;
    double Sin = sin(Rad);
    double Cos = cos(Rad);
    const double PI = 3.1415926535897932;

    CArray<POINT, POINT&> pnts;
    pnts.SetSize(0, 128);

    double X, Y;
    int X1, Y1;
    double K1, K2, K3, K4;
    CPoint pnt;
 
    double ang, range=2.0*PI, step=range/((nPrim+nSec)*4);

    for (ang=0.0; ang<range; ang += step) 
    {
        X = ((double)nPrim) * cos(ang);
        Y = ((double)nSec) * sin(ang);

        K1 = X * Sin; K2 = X * Cos; K3 = Y * Sin; K4 = Y * Cos;
        X1 = int(K2 - K3);
        Y1 = int(K1 + K4);
     
        pnt.x = x + X1;
        pnt.y = y - Y1;

        pnts.Add(pnt);
    }

    pnts.Add(pnts[0]);

    if(bFill)
    {
        N2DrawShape(pnts.GetData(), pnts.GetSize(), nFill, pDraw);
    }

    N2DrawLine(pnts.GetData(), pnts.GetSize(), nColor, pDraw);
}

